package com.unlcn.ils.kas.util;

import com.alibaba.fastjson.JSON;
import com.unlcn.ils.kas.annotation.LogRecord;
import com.unlcn.ils.kas.dao.model.sysadmin.SysLog;
import com.unlcn.ils.kas.service.security.domains.JwtUser;
import com.unlcn.ils.kas.service.sysadmin.SysLogService;
import com.unlcn.ils.kas.vo.RestVO;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

/**
 * AOP拦截记录日志
 * @author 罗必量
 */
@Aspect
@Component
public class LogInterceptionConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogInterceptionConfig.class);

    @Autowired
    private SysLogService sysLogService;

    /**
     * 实例化全局log实体，以便执行前拦截和执行后拦截共享信息
     */
    private SysLog sysLog = new SysLog();

    @Pointcut("@annotation(com.unlcn.ils.kas.annotation.LogRecord)")
    public void controllerAspect() {

    }

    /**
     * 请求执行前拦截
     * @param joinPoint
     */
    @Before(value = "controllerAspect()")
    public void methodBefore(JoinPoint joinPoint){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        LOGGER.info("请求地址" + request.getRequestURL() + "，执行前记录日志");

        // 获取操作描述信息
        String description = getLogRecordDescription(joinPoint);
        if (description == null) {
            sysLog.setDescription("");
        } else {
            sysLog.setDescription(description);
        }

        // 获取请求地址
        if (request.getRequestURL() == null) {
            sysLog.setUrl("");
        } else {
            sysLog.setUrl(request.getRequestURL().toString());
        }

        sysLog.setStartTime(new Date().getTime());

        // 获取baseUrl和uri
        String url = null;
        String baseUrl = null;
        String uri = null;
        if (request.getRequestURL() == null) {
            sysLog.setBasePath("");
            sysLog.setUri("");
        } else {
            url = request.getRequestURL().toString();
            Integer domainNameStartLength = url.indexOf("://") + 3;
            baseUrl = url.substring(domainNameStartLength, url.substring(url.indexOf("://")+3, url.length()).indexOf("/") + domainNameStartLength);
            uri = url.substring(url.indexOf(baseUrl)+baseUrl.length(), url.length());
            sysLog.setBasePath(baseUrl);
            sysLog.setUri(uri);
        }

        // 获取请求方式
        if (request.getMethod() == null) {
            sysLog.setMethod("");
        } else {
            sysLog.setMethod(request.getMethod());
        }

        // 获取请求参数
        if (request.getParameterMap() == null) {
            sysLog.setParameter("");
        } else {
            Map<String, String[]> paramMap = request.getParameterMap();
            String params = JSON.toJSONString(paramMap);
            sysLog.setParameter(params);
        }

        // 获取请求客户端的ip
        String ip = getIpAddress(request);
        if (ip == null) {
            sysLog.setIp("");
        } else {
            sysLog.setIp(ip);
        }


        JwtUser jwtUser = getJwtUser();
        if(!Objects.equals(jwtUser,null)){
            sysLog.setUsername(jwtUser.getUserName());
            sysLog.setUserAgent(jwtUser.getUserId().toString());
        }

        // 插入操作日志
        sysLogService.insert(sysLog);
    }

    /**
     * 请求执行后拦截
     * @param restVO
     */
    @AfterReturning(returning = "restVO",pointcut = "controllerAspect()")
    public void methodAfterReturing(RestVO restVO ){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

         if (restVO == null)
             return;

        LOGGER.info("请求地址" + sysLog.getUrl() + "，执行后更新请求结果；返回结果信息 {}" +restVO.toString());

        if (restVO == null)
            return;

        // 请求执行结果
        sysLog.setResult(restVO.getMessage());

        // 计算执行耗时
        if (sysLog.getStartTime() == null) {
            sysLog.setSpendTime(0);
        } else {
            Long currentTime = new Date().getTime();
            sysLog.setSpendTime(Integer.valueOf(String.valueOf(currentTime - sysLog.getStartTime())));
        }

        if (StringUtils.isEmpty(sysLog.getUsername()) && StringUtils.isEmpty(sysLog.getUserAgent())) {
            JwtUser jwtUser = getJwtUser();
            if(!Objects.equals(jwtUser,null)){
                sysLog.setUsername(jwtUser.getUserName());
                sysLog.setUserAgent(jwtUser.getUserId().toString());
            }
        }

        sysLogService.updateById(sysLog);
    }

    /**
     * 获取请求客户端IP
     * @param request
     * @return
     */
    private String getIpAddress(HttpServletRequest request){
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknow".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                // 根据网卡获取本机配置的IP地址
                InetAddress inetAddress = null;
                try {
                    inetAddress = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    LOGGER.error("LogInterceptionConfig.getIpAddress {}", e);
                    return null;
                }
                ipAddress = inetAddress.getHostAddress();
            }
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实的IP地址，多个IP按照','分割
        if(null != ipAddress && ipAddress.length() > 15){
            if(ipAddress.indexOf(",") > 0){
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }

        return ipAddress;
    }

    /**
     * 获取请求接口的描述信息
     * @param joinPoint
     * @return
     */
    private String getLogRecordDescription(JoinPoint joinPoint)  {
        try {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String description = "";
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        description = method.getAnnotation(LogRecord.class).description();
                        break;
                    }
                }
            }

            return description;
        } catch (Exception e) {
            LOGGER.error("LogInterceptionConfig.getLogRecordDescription {}", e);
            return null;
        }
    }

    private JwtUser getJwtUser(){
        JwtUser jwtUser = null;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            Object principal = authentication.getPrincipal();
            if (JwtUser.class.isInstance(principal)) {
                jwtUser = (JwtUser)principal;
            }
        }
        return jwtUser;
    }
}
